package sim16;

/**
 * The instruction class. Represents instructions and their data.
 * @author Piotr
 *
 */
class Instruction {
	final int address;		// The address of the instruction in IM
	final String bitString;	// The instruction as a string of bits
	final String textString;	// The instruction in mnemonic form
	InstructionClass iClass;	// The class of the instruction
	InstructionType iType;	// The type of the instruction
	int dest1Reg;	// Destination register number (-1 if no write back)
	int dest2Reg;	//
	int arg1Reg;	// First source register
	int arg2Reg;	// Second source register (-1 for if not applicable)
	private int dest1Val;	// Corresponding values
	private int dest2Val;
	int arg1Val;
	int arg2Val;	// or immediate value for i-type
	int arg3Val;	// Only used for offset on a store
	int memAddr;	// memory address for load/stores, jump target for branches
	boolean branchTaken; //Is the branch taken
	int dest1Ready;		// Countdown to when values are available for forwarding
	int dest2Ready;		// 0 = ready, 1 = ready next cycle, -1 = not computed
	boolean arg1Ready;	// Status of whether arguments are ready, used for
	boolean arg2Ready;	// hazard detection. True = values ready
						// False = hazard prevented this from loading, try forwarding
	


	/**
	 * Creates a no-op instruction at the specified PC. 
	 * @param	PC	The address for the instruction
	 */
	public Instruction(int pc) {
		address = pc;
		bitString = new String("0000000000000000");
		textString = new String("nop");
		dest1Ready = -1;
		dest2Ready = -1;
		arg1Ready = false;
		arg2Ready = false;
	}
	
	/**
	 * Creates an instruction object at the specified PC with the specified contents.
	 * @param addr the address of the instruction
	 * @param bitString the string of bits representing the machine code of this instruction
	 * @param textString the human-readable assembly language version of this instruction
	 */
	public Instruction(int addr, String bitString, String textString) {
		address = addr;
		this.bitString = bitString;
		this.textString = textString;
		dest1Ready = -1;
		dest2Ready = -1;
		arg1Ready = false;
		arg2Ready = false;
	}
	
	/**
	 * Returns true if the instruction is the defined nop instruction;
	 * @return
	 */
	public boolean isNop() {
		if (bitString.equals("0000000000000000")) {
			return true;
		} else {
			return false;
		}
	}
	
	/**
	 * Method to decrement value ready counters. Should be called at the beginning of
	 * the cycle in EX, MEM, and WB stages.
	 */
	public void cycle() {
		dest1Ready = (dest1Ready == 1 ? 0 : dest1Ready);
		dest2Ready = (dest2Ready == 1 ? 0 : dest2Ready);
	}
	
	/**
	 * Mutator for dest1Val. Automatically sets dest1Ready.
	 * @param val
	 */
	public void setDest1Val(int val) {
		dest1Val = val;
		dest1Ready = 1;
	}
	
	/**
	 * Mutator for dest2Val. Automatically sets dest2Ready.
	 * @param val
	 */
	public void setDest2Val(int val) {
		dest2Val = val;
		dest2Ready = 1;
	}
	
	/**
	 * Accessor for dest1Val.
	 * @param val
	 */
	public int getDest1Val() {
		return dest1Val;
	}
	
	/**
	 * Accessor for dest2Val.
	 * @param val
	 */
	public int getDest2Val() {
		return dest2Val;
	}
	
	public String toString() {
		StringBuilder sb = new StringBuilder();
		sb.append("Address: " + address + "\tCode: " + bitString);
		sb.append("\nDescription: " + textString);
		sb.append("\nClass: " + iClass + "\tType: " + iType);
		sb.append("\nD1Reg: " + dest1Reg + "\tD2Reg: " + dest2Reg 
				+ "\tA1Reg: " + arg1Reg + "\tA2Reg: " + arg2Reg);
		sb.append("\nD1Val: " + dest1Val + "\tD2Val: " + dest2Val 
				+ "\tA1Val: " + arg1Val + "\tA2Val: " + arg2Val);
		sb.append("\nD1Ready: " + dest1Ready + "\tD2Ready: " + dest2Ready
				+ "\tA1Ready: " + arg1Ready + "\tA2Ready: " + arg2Ready);
		sb.append("\nBranchTaken: " + branchTaken + "\tTargetAddr: " + memAddr);
		return sb.toString();
	}

}
